package queue.concurrent;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;

public class LinkedQueue<E> {
	static class Node<T> {
		T data;
		Node<T> next;

		public Node(T data, Node<T> next) {
			this.data = data;
			this.next = next;
		}
	}

	private Node<E> head;
	private Node<E> tail;
	private ReentrantLock lock = new ReentrantLock();

	public LinkedQueue() {
		Node<E> newNode = new Node<>(null, null);
		tail = head = newNode;
	}

	public void offer(E e) {
		Node<E> newNode = new Node<>(e, null);
		try {
			lock.lock();
			tail.next = newNode;
			tail = newNode;
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public E poll() {
		try {
			lock.lock();
			Node<E> h = head;
			Node<E> next = h.next;
			if (next == null) {
				return null;
			} else {
				head = next;
				h.data = null;
				h.next = null;
				return next.data;
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			lock.unlock();
		}
		return null;
	}
	
	static int count = 0;
	static int count2 = 0;
	static ArrayList<Thread> threads = new ArrayList<>();
	public static void main(String[] args) throws Exception {
		ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
//		AtomicInteger count = new AtomicInteger();
//		AtomicInteger count2 = new AtomicInteger();
		

		for (int i = 0; i < 1000; i++) {
			Thread t = new Thread(() -> {
				for (int j = 0; j < 20; j++) {
					
					int c;
					synchronized (queue) {
						c = ++count;
					}
					queue.offer(c);
					// System.out.println("put" + c + ":" + data);
					/*
					 * try { Thread.sleep(20); } catch (InterruptedException e) {
					 * e.printStackTrace(); }
					 */
					/*
					 * synchronized (queue) { if (c == 20000) { System.out.println("_20000"); break;
					 * } }
					 */
					
				}
			}, "t" + i);
			t.start();
		}

		// Thread.sleep(3000);
		// queue.print();

		for (int i = 0; i < 500; i++) {
			Thread t = new Thread(() -> {
				while (true) {

					Integer data = queue.poll();
					synchronized (queue) {
						if (data != null) {
							int c = ++count2;
							/*
							 * if (c == 20000) { System.out.println("20000"); break; }
							 */
						} else {
							break;
						}
					}
				}
			});
			t.start();
		}
		
		for (Thread t : threads) {
			t.join();
		}
		
		System.out.println(count);
		System.out.println(count2);
		/*
		 * int count = 0; while (true) { String data = queue.poll(); if (data != null) {
		 * ++count; System.out.println("get" + count + ":" + data); } else { break; } }
		 */
	}

}
